/*
 * HW1.c
 *
 * Created: 9/9/2012 6:12:06 PM
 *  Author: Evan Zimmerman <zimmermane@msoe.edu>
 *  Course: CE4920
 *
 * Purpose: This program uses a timer interrupt and pushbuttons
 *          to modify the state of two LEDs. When no buttons are
 *          pressed, LED0 blinks at 1 HZ at 50% duty cycle and LED1
 *          blinks at 1/6 Hz at 50% duty. If Button 0 is pressed, 
 *          the frequencies of the two LEDs are switched. If
 *          Button 1 is pressed, the frequency of the blinks is
 *          doubled on both LEDs. If both buttons are pressed,
 *          both LEDs remain on.
 */ 

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>

// global variables
uint8_t counter0;
uint8_t counter1;
bool button0Pressed;
bool button1Pressed;

/* Toggles the specified LED on PORTB (0-7)
 * If the LED is on, this turns it off but preserves the other bits on the port.
 * If the LED is off, this turns it on.
 */
void led_toggle(uint8_t LED)
{
	if(PINB & (1<<LED))           // if LED enabled
		PORTB = PINB & ~(1<<LED); // disable LED
	else
		PORTB = PINB | (1<<LED);  // enable LED
}

/* getPDxVal
 * Returns the value of the specified bit on PORTD (0 or 1)
 */
uint8_t getPDxVal(uint8_t PDx)
{
	uint8_t retVal = (PIND & (1<<PDx))>>PDx;
	return retVal;
}

/* Main Program Function:
 * Initializes ports, interrupts, and checks the pushbutton pins
 * for change in state
 */
int main(void)
{

	// initialize ddrb : all outputs (LEDs)
	DDRB = 0xFF;
	// initialize ddrd : all inputs (pushbuttons)
	DDRD = 0x00;
	PORTD = (1 << PD2) | (1 << PD3); // enable pullups on pushbuttons PD2/PD3
	
	// initialize timer1A to interrupt every 250ms (3906.25 clock ticks at clk/1024)
	OCR1AH = 0x0F; //3906
	OCR1AL = 0x42;
	TCNT1H = 0;  //start at 0
	TCNT1L = 0;
	// initialize tccr1b: CTC mode, clk/1024
	TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10);
	// enable Timer1 output compare A
	TIMSK = (1<<OCIE1A);
	
	// initialize global and local variables
	counter0 = 0;
	counter1 = 0;
	button0Pressed = false;
	button1Pressed = false;
	uint8_t pressed0_confidence_level = 0;
	uint8_t released0_confidence_level = 0;
	uint8_t pressed1_confidence_level = 0;
	uint8_t released1_confidence_level = 0;
	
	sei();
	
    while(1)
    {
		// If a zero was read on PD2, pushbutton 1 is pressed (or bouncing)
		// Otherwise pushbutton 1 is released (or bouncing)
		if(!getPDxVal(PD2))
		{
			pressed0_confidence_level++;
			// only register press if we are confident one occurred (string of zeros)
			if(pressed0_confidence_level > 200)
			{
				if(!button0Pressed)
					button0Pressed = true;
				pressed0_confidence_level = 0;
			}
		}
		else
		{
			released0_confidence_level++;
			// only register release if we are confident one occurred (string of ones)
			if(released0_confidence_level > 200)
			{
				if(button0Pressed)
					button0Pressed = false;
				released0_confidence_level = 0;
			}
		}
		
		// If a zero was read on PD3, pushbutton 2 is pressed (or bouncing)
		// Otherwise pushbutton 2 is released (or bouncing)
		if(!getPDxVal(PD3))
		{
			pressed1_confidence_level++;
			// only register press if we are confident one occurred (string of zeros)
			if(pressed1_confidence_level > 200)
			{
				if(!button1Pressed)
					button1Pressed = true;
				pressed1_confidence_level = 0;
			}
		}
		else
		{
			released1_confidence_level++;
			// only register release if we are confident one occurred (string of ones)
			if(released1_confidence_level > 200)
			{
				if(button1Pressed)
					button1Pressed = false;
				released1_confidence_level = 0;
			}
		}
    }
}

/* Timer1 Output Compare A ISR
 * Timer interrupt for LED operations
 * Interrupt occurs every 250ms (fastest possible blink time)
 */
ISR(TIMER1_COMPA_vect)
{
	counter0++;
	counter1++;
	// normal toggle speeds of the LEDs (LED0 = 500ms, LED1 = 3000ms)
	if((!button0Pressed) && (!button1Pressed))
	{
		if(counter0 >= 2) // 500 ms
		{
			counter0 = 0;
			led_toggle(PB0);
		}
		
		if(counter1 >= 12) // 3 s
		{
			counter1 = 0;
			led_toggle(PB1);
		}
	}
	// swap the toggle speeds of the LEDs (LED0 = 3000ms, LED1 = 500ms)
	else if(button0Pressed && (!button1Pressed))
	{
		if(counter0 >= 2) // 500 ms
		{
			counter0 = 0;
			led_toggle(PB1);
		}
		
		if(counter1 >= 12) // 3 s
		{
			counter1 = 0;
			led_toggle(PB0);
		}
	}
	// double the toggle speeds of the LEDs (LED0 = 250ms, LED1 = 1500ms)
	else if((!button0Pressed) && button1Pressed)
	{
		if(counter0 >= 1) // 250 ms
		{
			counter0 = 0;
			led_toggle(PB0);
		}
		
		if(counter1 >= 6) // 1.5 s
		{
			counter1 = 0;
			led_toggle(PB1);
		}
	}	
	// turn on both LEDs
	else
		PORTB = PINB | (1<<PB0) | (1<<PB1);
}